//
// Created by LiuYou on 2021/8/20.
//

#ifndef HIGHPERFORMANCELINUXSERVERPROGRAMMING_LINUX2_HTTPCONNECT2_HPP
#define HIGHPERFORMANCELINUXSERVERPROGRAMMING_LINUX2_HTTPCONNECT2_HPP


#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include "HttpConnectNeed.hpp"


extern int setFdNonblocking(int fd);

extern void addFdToEpoll(int epollFd, int fd, bool oneShot);

extern void removeFd(int epollFd, int fd);

extern void modFd(int epollFd, int fd, EPOLL_EVENTS events);

class HttpConnect {
private:
    /** 初始化连接。内部使用。*/
    void init();

    /** 解析HTTP请求。
     * <p>
     * 主状态机。*/
    HttpCode processRead();

    LineStatus parseLine();

    char* getLine();

    HttpCode parseRequestLine(char* text);

    HttpCode parseHeaders(char* text);

    HttpCode doRequest();

    HttpCode parseContent(char* text);

    bool processWrite(HttpCode code);

    bool addStatusLine(int status, const char* title);

    bool addResponse(const char* format, ...);

    bool addHeaders(size_t contentLength);

    bool addContentLength(size_t contentLength);

    bool addLinger();

    bool addBlankLine();

    bool addContent(const char* content);

    void unmap();

public:
    /** 初始化连接。*/
    void init(int connectionFd, const sockaddr_in& address);

    /** 解析HTTP请求。*/
    void closeConnection(bool realClose = true);

    /** 处理客户请求。
     * <p>
     * 由线程池中的工作线程调用, 这是处理 HTTP 请求的入口函数。*/
    void process();

    /** 非阻塞读操作。*/
    bool read();

    /** 非阻塞写操作。*/
    bool write();

public:
    HttpConnect();

    ~HttpConnect();

public:
    /** 所有socket上的事件都被注册到同一个epoll内核时间表中, 所以将epoll文件描述符设置为静态的。*/
    static int epollFd;

    /** 统计用户数量。*/
    static int userCount;

private:
    /** 该HTTP连接的socket。*/
    int connectFd{};

    /** 对方的socket地址。*/
    sockaddr_in remoteAddress{};

public:
    /** 读缓冲区的大小。*/
    static const int READ_BUFFER_SIZE = 2048;

private:
    /** 读缓冲区。*/
    char readBuf[READ_BUFFER_SIZE]{};

    /** 标识读缓冲中已经读入的客户数据的最后一个字节的下一个位置。*/
    int readIdx{};

    /** 当前正在分析的字符在读缓冲区中的位置。*/
    int checkedIdx{};

    /** 当前正在解析的行的起始位置。*/
    int startLine{};

public:
    /** 写缓冲区的大小。*/
    static const int WRITE_BUFFER_SIZE = 1024;

private:
    /** 写缓冲区。*/
    char writeBuf[WRITE_BUFFER_SIZE]{};

    /** 写缓冲区中待发送的字节数。*/
    int writeIdx{};

    /** 主状态机当前所处的状态。*/
    CheckState checkState;

    /** 请求方法。*/
    Method method;

public:
    /** 文件名的最大长度。*/
    static const int FILE_NAME_LEN = 200;

private:
    /** 客户请求的目标文件的完整路径, 其内容等于 doc_root + m_url, doc_root是网站根目录。*/
    char realFile[FILE_NAME_LEN]{};

    /** 客户请求的目标文件的文件名。*/
    char* url{};

    /** HTTP协议版本号, 我们仅支持 HTTP 1.1。*/
    char* version{};

    /** 主机名。*/
    char* host{};

    /** HTTP请求的消息体的长度。*/
    int contentLength{};

    /** HTTP请求是否要求保持连接。*/
    bool linger{};

    /** 客户请求的目标文件被 mmap()到内存中的起始位置。*/
    char* fileAddress{};

    /** 目标文件的状态。通过它我们可以判断文件是否存在、是否为目录、是否可读, 并获取文件大小等信息。*/
    struct stat fileStat{};

    /* 我们将采用 writev()来执行写操作, 所以定义下面两个成员: */

    /** */
    iovec ioVector[2]{};

    /** 表示被写内存块的数量。*/
    int ioVectorCount{};

};


#endif //HIGHPERFORMANCELINUXSERVERPROGRAMMING_LINUX2_HTTPCONNECT2_HPP
